module ultraMips(/*AUTOARG*/
   // Outputs
   inst_o_valid, inst_o_addr, mem_o_data, mem_o_addr, mem_o_dest_reg,
   mem_o_ctrl, mem_o_line_valid,
   // Inputs
   clk, resetn, inst_i_valid, inst_i_line, mem_i_line, mem_i_dest_m,
   mem_i_ctrl_m, mem_i_line_valid_m
   );

input clk;
input resetn;
input inst_i_valid;
input [63:0] inst_i_line;
	
input [63:0] mem_i_line;
input [4:0] mem_i_dest_m;
input mem_i_ctrl_m;
input [3:0] mem_i_line_valid_m;

output inst_o_valid;
output [31:0] inst_o_addr;

output [31:0] mem_o_data;
output [31:0] mem_o_addr;
output [4:0] mem_o_dest_reg;
output [1:0] mem_o_ctrl;
output [3:0] mem_o_line_valid;

//WIRES
wire [31:0] regA_w_dataA;
wire [31:0] regA_w_dataB;
wire [31:0] regB_w_dataA;
wire [31:0] regB_w_dataB;
wire [31:0] regA_w_immediate;
wire [31:0] regB_w_immediate;
wire [3:0] regA_w_tag;
wire [3:0] regB_w_tag;
wire [4:0] regA_w_dst_reg;
wire [4:0] regB_w_dst_reg;
wire [5:0] regA_w_ctrl_signals;
wire [3:0] regA_mem_w_valid;
wire [5:0] regB_w_ctrl_signals;
wire [3:0] regB_mem_w_valid;


wire [31:0] ex_w_data;
wire [31:0] ex_w_addr;
wire [2:0] ex_w_ctrl;
wire [5:0] ex_w_tag;
wire [4:0] ex_w_dst_reg;
wire [1:0] ex_w_branch_nt;
//wire [3:0] regB_mem_ex_w_valid;

wire [4:0] mem_w_fin_dst_reg;
wire [31:0] mem_w_fin_data;
wire mem_w_fin_ctrl;
wire mem_w_hazard;
wire [3:0] mem_w_fin_valid;

reg [4:0] wbB_w_addr;
reg [31:0] wbB_w_data;
reg wbB_w_ctrl;

wire [4:0] exA_w_dst_reg;
wire [31:0] exA_w_data;
wire [31:0] exA_w_addr;
wire [3:0] regA_mem_ex_w_valid;

wire [4:0] exB_w_dst_reg;
wire [31:0] exB_w_data;
wire [31:0] exB_w_addr;
wire [3:0] regB_mem_ex_w_valid;
wire [2:0] exB_w_ctrl;
wire [31:0] mem_w_fin_dst_data;

wire full;
wire [31:0] inst1,inst0;
wire inst1_valid,inst0_valid;
wire rs1_valid;
wire rt1_valid;
wire rd1_valid;
wire branch_dep1;
wire [3:0] branch_count1;
wire mem_tag1;
wire [3:0]mem_count1;
wire rs0_valid;
wire rt0_valid;
wire rd0_valid;
wire branch_dep0;
wire [3:0] branch_count0;
wire mem_tag0;
wire [3:0]mem_count0;
wire rs0_dep;
wire rt0_dep;
reg [1:0] branch_decision;
wire [1:0]branch_decisionA,branch_decisionB;
reg [3:0]br_count;
wire [3:0] br_countA,br_countB;
wire [4:0]rd1_broadcast;
wire [4:0]rd0_broadcast;
wire rd1valid_broadcast;
wire rd0valid_broadcast;
wire [31:0]inst_A_out;
wire [31:0]inst_B_out;

wire [3:0]branch_countA;
wire [3:0]branch_countB;

wire [4:0] trash5,trash4,trash3,trash2,trash1,trash0;
wire [1:0] inst_dep;
wire xor1,xor2;

assign inst_dep={rs0_dep,rt0_dep};

fetch fetch_stage (.i_clk(clk), .i_rst(~resetn),
	      .i_inst_line(inst_i_line), .i_inst_line_val(inst_i_valid),
	      .i_haz_queue(full), .i_haz_branch(branch_decision),
	      .o_inst_addr(inst_o_addr), .o_inst_addr_val(inst_o_valid),
	      .o_inst_A(inst1), .o_inst_A_val(inst1_valid),
	      .o_inst_A_btag(branch_count1), .o_inst_A_btag_val(branch_dep1),
	      .o_inst_A_mtag(mem_count1), .o_inst_A_mtag_val(mem_tag1),
	      .o_inst_A_rs(trash1), .o_inst_A_rs_val(rs1_valid),
	      .o_inst_A_rt(trash0), .o_inst_A_rt_val(rt1_valid),
	      .o_inst_B(inst0), .o_inst_B_val(inst0_valid),
	      .o_inst_B_btag(branch_count0), .o_inst_B_btag_val(branch_dep0),
	      .o_inst_B_mtag(mem_count0), .o_inst_B_mtag_val(mem_tag0),
	      .o_inst_B_rs(trash3), .o_inst_B_rs_val(rs0_valid),
	      .o_inst_B_rt(trash2), .o_inst_B_rt_val(rt0_valid),
	      .o_inst_dep(inst_dep),.o_inst_A_rd(trash5), .o_inst_A_rd_val(rd1_valid),
		  .o_inst_B_rd(trash4), .o_inst_B_rd_val(rd0_valid));
	
	

issue_stage issue (.clk(clk),
.resetn(resetn),
.inst1(inst1),
.inst1_valid(inst1_valid),
.rs1_valid (rs1_valid),
.rt1_valid (rt1_valid),
.rd1_valid (rd1_valid), // not present
.branch_dep1(branch_dep1),
.branch_count1(branch_count1),
.mem_tag1(mem_tag1),
.mem_count1(mem_count1),
.inst0(inst0),
.inst0_valid(inst0_valid),
.rs0_valid(rs0_valid),
.rt0_valid(rt0_valid),
.rd0_valid(rd0_valid), //doesnt send
.branch_dep0(branch_dep0),
.branch_count0(branch_count0),
.mem_tag0(mem_tag0),
.mem_count0(mem_count0),
.rs0_dep(rs0_dep),
.rt0_dep(rt0_dep),
.branch_decision(branch_decision),
.br_count(br_count),
.rd1_broadcast(rd1_broadcast),
.rd0_broadcast(rd0_broadcast),
.rd1valid_broadcast(rd1valid_broadcast),
.rd0valid_broadcast(rd0valid_broadcast),
.inst_A_out(inst_A_out),
.inst_B_out(inst_B_out),
.full(full),
.branch_countA(branch_countA),
.branch_countB(branch_countB),
.hazard(mem_w_hazard));

assign rd1_broadcast=exA_w_dst_reg;
assign rd1valid_broadcast=ex_w_ctrl[0];
assign rd0_broadcast=wbB_w_addr; 
assign rd0valid_broadcast=wbB_w_ctrl;
assign xor1=branch_decisionA[0]^branch_decisionA[1];
assign xor2=branch_decisionB[0]^branch_decisionB[1];

always @ ({xor1,xor2})
begin
case({xor1,xor2})
2'b00:branch_decision=2'b00;
2'b01:branch_decision=branch_decisionB;
2'b10:branch_decision=branch_decisionA;
2'b11:branch_decision=2'b00;
endcase
end

always @ ({xor1,xor2})
begin
case({xor1,xor2})
2'b00:br_count=4'b0000;
2'b01:br_count=br_countB;
2'b10:br_count=br_countA;
2'b11:br_count=4'b0000;
endcase
end



decode_unit dec1(
   // Outputs
   .regA_o_dataA(regA_w_dataA), .regA_o_dataB(regA_w_dataB), .regB_o_dataA(regB_w_dataA), .regB_o_dataB(regB_w_dataB),
   .regA_o_immediate(regA_w_immediate), .regB_o_immediate(regB_w_immediate), .regA_o_tag(regA_w_tag), .regB_o_tag(regB_w_tag),
   .regA_o_dst_reg(regA_w_dst_reg), .regB_o_dst_reg(regB_w_dst_reg), .regA_o_ctrl_signals(regA_w_ctrl_signals),
   .regA_mem_o_valid(regA_mem_w_valid), .regB_o_ctrl_signals(regB_w_ctrl_signals), .regB_mem_o_valid(regB_mem_w_valid),
   // Inputs
   .clk(clk), .reset(resetn), .regA_i_inst(inst_A_out), .regA_i_tag(branch_countA), .regA_i_haz(mem_w_hazard), 
.regB_i_inst(inst_B_out),
   .regB_i_tag(branch_countB), .regB_i_haz(mem_w_hazard),
   .wbA_i_addr(exA_w_dst_reg), .wbA_i_data(exA_w_data), .wbA_i_ctrl(ex_w_ctrl[0]),
   .wbB_i_addr(wbB_w_addr), .wbB_i_data(wbB_w_data), .wbB_i_ctrl(wbB_w_ctrl)
   );


execution exA(
   // Outputs
   .ex_o_data(exA_w_data), .ex_o_addr(exA_w_addr), .ex_o_ctrl(ex_w_ctrl), .ex_o_tag(br_countA), .ex_o_dst_reg(exA_w_dst_reg),
   .ex_o_branch_nt(branch_decisionA), .regB_mem_o_valid(regA_mem_ex_w_valid), // 0th bit of ex_w_ctrl
   // Inputs
   .clk(clk), .reset(resetn), .ex_i_valueA(regA_w_dataA), .ex_i_valueB(regA_w_dataB), .ex_i_immediate(regA_w_immediate), .ex_i_tag(regA_w_tag),
   .ex_i_dst_reg(regA_w_dst_reg), .ex_i_ctrl(regA_w_ctrl_signals), .regB_mem_i_valid(regA_mem_w_valid), .ex_i_hazard(mem_w_hazard)
   );
   

execution exB(
   // Outputs
   .ex_o_data(exB_w_data), .ex_o_addr(exB_w_addr), .ex_o_ctrl(exB_w_ctrl), .ex_o_tag(br_countB), .ex_o_dst_reg(exB_w_dst_reg),
   .ex_o_branch_nt(branch_decisionB), .regB_mem_o_valid(regB_mem_ex_w_valid),
   // Inputs
   .clk(clk), .reset(resetn), .ex_i_valueA(regB_w_dataA), .ex_i_valueB(regB_w_dataB), .ex_i_immediate(regB_w_immediate), .ex_i_tag(regB_w_tag),
   .ex_i_dst_reg(regB_w_dst_reg), .ex_i_ctrl(regB_w_ctrl_signals), .regB_mem_i_valid(regB_mem_w_valid), .ex_i_hazard(mem_w_hazard)
   );
   
memory memB(
   // Outputs
   .mem_o_data(mem_o_data), .mem_o_addr(mem_o_addr), .mem_o_dest_reg(mem_o_dest_reg), .mem_o_ctrl(mem_o_ctrl),
   .mem_o_line_valid(mem_o_line_valid),
   .mem_o_fin_dst_reg(mem_w_fin_dst_reg), .mem_o_fin_data(mem_w_fin_dst_data), .mem_o_fin_ctrl(mem_w_fin_ctrl), 
   .mem_o_hazard(mem_w_hazard), .mem_o_fin_valid(mem_w_fin_valid),
   // Inputs
   .clk(clk), .reset(resetn), .mem_i_data_ex(exB_w_data), .mem_i_addr_ex(exB_w_addr), .mem_i_dest_reg_ex(exB_w_dst_reg),
   .mem_i_ctrl_ex(exB_w_ctrl), .mem_i_line_valid_ex(regB_mem_ex_w_valid), 
   .mem_i_line(mem_i_line), .mem_i_dest_m(mem_i_dest_m), .mem_i_ctrl_m(mem_i_ctrl_m), .mem_i_line_valid_m(mem_i_line_valid_m)
   );

//selection for Wb of pipeB....from exB or memB?
always @(mem_w_hazard)
begin
case(mem_w_hazard)
1:  begin
	wbB_w_addr = mem_w_fin_dst_reg;
	wbB_w_data = mem_w_fin_dst_data;
	wbB_w_ctrl = mem_w_fin_ctrl;
  end
//else  
0:  begin
	wbB_w_addr = exB_w_dst_reg;
	wbB_w_data = exB_w_data;
	wbB_w_ctrl =  (~exB_w_ctrl[1]) & exB_w_ctrl[0];
  end
endcase
end

endmodule

